{"nbformat":4,"nbformat_minor":0,"metadata":{"colab":{"name":"Training_a_Normalizing_Flow_on_QM9.ipynb","provenance":[],"collapsed_sections":[],"toc_visible":true,"authorship_tag":"ABX9TyNyrrTvEu36LoiXsM0a4+4b"},"kernelspec":{"name":"python3","display_name":"Python 3"}},"cells":[{"cell_type":"markdown","metadata":{"id":"8BrLuyU3kMdt","colab_type":"text"},"source":["# Tutorial Part ??: Training a Normalizing Flow on QM9\n","By [Nathan C. Frey](https://ncfrey.github.io/) | [Twitter](https://twitter.com/nc_frey)\n","\n","\n","In this tutorial, we will train a Normalizing Flow (NF) on the [QM9 dataset](https://www.nature.com/articles/sdata201422). The dataset comprises 133,885 stable small organic molecules made up of CHNOF atoms. We will try to train a network that is an invertible transformation between a simple base distribution and the distribution of molecules in QM9.  One of the key advantages of normalizing flows is that they can be constructed to efficiently sample from a distribution (generative modeling) and do probability density calculations (exactly compute log-likelihoods), whereas other models make tradeoffs between the two or can only approximate probability densities.\n","\n","NFs are useful whenever we need a probabilistic model with one or both of these capabilities. Note that because NFs are completely invertible, there is no \"latent space\" in the sense used when referring to generative adversarial networks or variational autoencoders. For more on NFs, we refer to this [review paper](https://arxiv.org/pdf/1912.02762.pdf).\n","\n","\n","To encode the QM9 dataset, we'll make use of the SELFIES (SELF-referencIng Embedded Strings) representation, which is a 100% robust molecular string representation. SMILES strings produced by generative models are often syntactically invalid (they do not correspond to a molecular graph), or they violate chemical rules like the maximum number of bonds between atoms. SELFIES are designed so that even totally random SELFIES strings correspond to valid molecular graphs, so they are a great framework for generative modeling. For more details about SELFIES, see the [GitHub repo](https://github.com/aspuru-guzik-group/selfies) and the associated [paper](https://arxiv.org/abs/1905.13741).\n","\n","\n","## Colab\n","\n","This tutorial and the rest in this sequence are designed to be done in Google colab. If you'd like to open this notebook in colab, you can use the following link.\n","\n","[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/deepchem/deepchem/blob/master/examples/tutorials/23_Training_a_Normalizing_Flow_on_QM9.ipynb)\n","\n","## Setup\n","\n","To run DeepChem within Colab, you'll need to run the following cell of installation commands. This will take about 5 minutes to run to completion and install your environment."]},{"cell_type":"code","metadata":{"id":"06FZl9Nqj_jq","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":319},"executionInfo":{"status":"ok","timestamp":1600972940078,"user_tz":240,"elapsed":124245,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"e1d3f749-00ef-4b81-899e-99a66e4d737e"},"source":["!curl -Lo conda_installer.py https://raw.githubusercontent.com/deepchem/deepchem/master/scripts/colab_install.py\n","import conda_installer\n","conda_installer.install()\n","!/root/miniconda/bin/conda info -e"],"execution_count":1,"outputs":[{"output_type":"stream","text":["  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n","                                 Dload  Upload   Total   Spent    Left  Speed\n","100  3490  100  3490    0     0  15863      0 --:--:-- --:--:-- --:--:-- 15863\n"],"name":"stdout"},{"output_type":"stream","text":["add /root/miniconda/lib/python3.6/site-packages to PYTHONPATH\n","python version: 3.6.9\n","fetching installer from https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh\n","done\n","installing miniconda to /root/miniconda\n","done\n","installing rdkit, openmm, pdbfixer\n","added conda-forge to channels\n","added omnia to channels\n","done\n","conda packages installation finished!\n"],"name":"stderr"},{"output_type":"stream","text":["# conda environments:\n","#\n","base                  *  /root/miniconda\n","\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"id":"dVXJOn-p8Pld","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":358},"executionInfo":{"status":"ok","timestamp":1600972946086,"user_tz":240,"elapsed":130228,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"8ec60872-e04f-4488-e0fd-9f1f16ccd670"},"source":["!pip install --pre deepchem\n","import deepchem\n","deepchem.__version__"],"execution_count":2,"outputs":[{"output_type":"stream","text":["Collecting deepchem\n","\u001b[?25l  Downloading https://files.pythonhosted.org/packages/20/13/03547ffeca81a4d65fef79d0d13bd2eaf541408c606bbdbd67c0e2fec0b2/deepchem-2.4.0rc1.dev20200923221715.tar.gz (393kB)\n","\r\u001b[K     |▉                               | 10kB 16.1MB/s eta 0:00:01\r\u001b[K     |█▋                              | 20kB 1.8MB/s eta 0:00:01\r\u001b[K     |██▌                             | 30kB 2.1MB/s eta 0:00:01\r\u001b[K     |███▎                            | 40kB 2.4MB/s eta 0:00:01\r\u001b[K     |████▏                           | 51kB 1.8MB/s eta 0:00:01\r\u001b[K     |█████                           | 61kB 2.1MB/s eta 0:00:01\r\u001b[K     |█████▉                          | 71kB 2.3MB/s eta 0:00:01\r\u001b[K     |██████▋                         | 81kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████▌                        | 92kB 2.7MB/s eta 0:00:01\r\u001b[K     |████████▎                       | 102kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████▏                      | 112kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████                      | 122kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████▉                     | 133kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████▋                    | 143kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████▌                   | 153kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████▎                  | 163kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████▏                 | 174kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████                 | 184kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████▉                | 194kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████▋               | 204kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████████▌              | 215kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████████▎             | 225kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████████▏            | 235kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████████            | 245kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████████▉           | 256kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████████████▋          | 266kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████████████▌         | 276kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████████████▎        | 286kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████████████▏       | 296kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████████████████       | 307kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████████████████▉      | 317kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████████████████▋     | 327kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████████████████▌    | 337kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████████████████▎   | 348kB 2.6MB/s eta 0:00:01\r\u001b[K     |█████████████████████████████▏  | 358kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████████████████████  | 368kB 2.6MB/s eta 0:00:01\r\u001b[K     |██████████████████████████████▉ | 378kB 2.6MB/s eta 0:00:01\r\u001b[K     |███████████████████████████████▋| 389kB 2.6MB/s eta 0:00:01\r\u001b[K     |████████████████████████████████| 399kB 2.6MB/s \n","\u001b[?25hRequirement already satisfied: joblib in /usr/local/lib/python3.6/dist-packages (from deepchem) (0.16.0)\n","Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from deepchem) (1.18.5)\n","Requirement already satisfied: pandas in /usr/local/lib/python3.6/dist-packages (from deepchem) (1.0.5)\n","Requirement already satisfied: scikit-learn in /usr/local/lib/python3.6/dist-packages (from deepchem) (0.22.2.post1)\n","Requirement already satisfied: scipy in /usr/local/lib/python3.6/dist-packages (from deepchem) (1.4.1)\n","Requirement already satisfied: python-dateutil>=2.6.1 in /usr/local/lib/python3.6/dist-packages (from pandas->deepchem) (2.8.1)\n","Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.6/dist-packages (from pandas->deepchem) (2018.9)\n","Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/dist-packages (from python-dateutil>=2.6.1->pandas->deepchem) (1.15.0)\n","Building wheels for collected packages: deepchem\n","  Building wheel for deepchem (setup.py) ... \u001b[?25l\u001b[?25hdone\n","  Created wheel for deepchem: filename=deepchem-2.4.0rc1.dev20200924184214-cp36-none-any.whl size=499031 sha256=bb06a3dd7f6e432a05d18f54403bc00e292ddc9c52b8d5008c640c3153b917ee\n","  Stored in directory: /root/.cache/pip/wheels/c7/5c/0b/1f5cfa9461cf4af4190b45b7ae87ecc22ee5bdfb55748cfbe3\n","Successfully built deepchem\n","Installing collected packages: deepchem\n","Successfully installed deepchem-2.4.0rc1.dev20200924184214\n"],"name":"stdout"},{"output_type":"execute_result","data":{"application/vnd.google.colaboratory.intrinsic+json":{"type":"string"},"text/plain":["'2.4.0-rc1.dev'"]},"metadata":{"tags":[]},"execution_count":2}]},{"cell_type":"markdown","metadata":{"id":"OGVYBZh6Gq7N","colab_type":"text"},"source":["Install the SELFIES library to translate SMILES strings."]},{"cell_type":"code","metadata":{"id":"sqEygLk5GLYF","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":322},"executionInfo":{"status":"ok","timestamp":1600972951697,"user_tz":240,"elapsed":135821,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"3df1490a-f23f-4ffc-8398-3dcc27770948"},"source":["!git clone https://github.com/aspuru-guzik-group/selfies.git\n","%cd selfies\n","!pip install .\n","%cd .."],"execution_count":3,"outputs":[{"output_type":"stream","text":["Cloning into 'selfies'...\n","remote: Enumerating objects: 157, done.\u001b[K\n","remote: Counting objects: 100% (157/157), done.\u001b[K\n","remote: Compressing objects: 100% (114/114), done.\u001b[K\n","remote: Total 2026 (delta 90), reused 85 (delta 43), pack-reused 1869\u001b[K\n","Receiving objects: 100% (2026/2026), 12.38 MiB | 17.27 MiB/s, done.\n","Resolving deltas: 100% (1276/1276), done.\n","/content/selfies\n","Processing /content/selfies\n","Building wheels for collected packages: selfies\n","  Building wheel for selfies (setup.py) ... \u001b[?25l\u001b[?25hdone\n","  Created wheel for selfies: filename=selfies-1.0.1-cp36-none-any.whl size=27081 sha256=7c0b9aa7277c7a1103b657efc0354829b65851f2b2fa8e2082ae31544350be83\n","  Stored in directory: /tmp/pip-ephem-wheel-cache-lkv5aj3a/wheels/d0/8b/6e/8a44d44da67fdb190acc4f94129ff1428fc623ff9ad9a7abed\n","Successfully built selfies\n","Installing collected packages: selfies\n","Successfully installed selfies-1.0.1\n","/content\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"id":"FpqPgmalHCdb","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":70},"executionInfo":{"status":"ok","timestamp":1600972952463,"user_tz":240,"elapsed":136568,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"b1358a90-efea-45b3-8aff-0e43e82d46c0"},"source":["import numpy as np\n","import matplotlib.pyplot as plt\n","import seaborn as sns\n","import pandas as pd\n","import os\n","\n","import deepchem as dc\n","from deepchem.models.normalizing_flows import NormalizingFlow, NormalizingFlowModel\n","from deepchem.models.optimizers import Adam\n","from deepchem.data import NumpyDataset\n","from deepchem.splits import RandomSplitter\n","from deepchem.molnet import load_tox21\n","\n","import rdkit\n","from rdkit.Chem import Draw\n","\n","from IPython.display import Image, display\n","\n","import selfies as sf\n","\n","import tensorflow as tf\n","import tensorflow_probability as tfp\n","\n","tfd = tfp.distributions\n","tfb = tfp.bijectors\n","tfk = tf.keras\n","\n","tfk.backend.set_floatx('float64')"],"execution_count":4,"outputs":[{"output_type":"stream","text":["/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.\n","  import pandas.util.testing as tm\n"],"name":"stderr"}]},{"cell_type":"markdown","metadata":{"id":"XYRunI2yHoLS","colab_type":"text"},"source":["First, let's get a dataset of 2500 small organic molecules from the QM9 dataset. We'll then convert the molecules to SELFIES, one-hot encode them, and dequantize the inputs so they can be processed by a normalizing flow. 2000 molecules will be used for training, while the remaining 500 will be split into validation and test sets. We'll use the validation set to see how our architecture is doing at learning the underlying the distribution, and leave the test set alone. You should feel free to experiment with this notebook to get the best model you can and evaluate it on the test set when you're done!"]},{"cell_type":"code","metadata":{"id":"k2-L2gFHr04H","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973619044,"user_tz":240,"elapsed":803137,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["# Download from MolNet\n","tasks, datasets, transformers = dc.molnet.load_qm9(featurizer='ECFP')\n","df = pd.DataFrame(data={'smiles': datasets[0].ids})"],"execution_count":6,"outputs":[]},{"cell_type":"code","metadata":{"id":"fdo6CJMPGyig","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973619064,"user_tz":240,"elapsed":803152,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["data = df[['smiles']].sample(2500, random_state=42)"],"execution_count":7,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"ZMh-1QUqCxkY","colab_type":"text"},"source":["SELFIES defines a dictionary called `bond_constraints` that enforces how many bonds every atom or ion can make. E.g., 'C': 4, 'H': 1, etc. The `?` symbol is used for any atom or ion that isn't defined in the dictionary, and it defaults to 8 bonds. Because QM9 contains ions and we don't want to allow those ions to form up to 8 bonds, we'll constrain them to 3. This will really improve the percentage of valid molecules we generate. You can read more about setting constraints in the [SELFIES documentation](https://selfies-mirror.readthedocs.io/en/latest/selfies_examples.html#Advanced-Usage)."]},{"cell_type":"code","metadata":{"id":"6cOS0cNTdb0I","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":202},"executionInfo":{"status":"ok","timestamp":1600973619070,"user_tz":240,"elapsed":803140,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"d8fc1b88-0af7-4a82-e85c-889dbbcf8e86"},"source":["sf.set_semantic_constraints()  # reset constraints\n","constraints = sf.get_semantic_constraints()\n","constraints['?'] = 3\n","\n","sf.set_semantic_constraints(constraints)\n","constraints"],"execution_count":8,"outputs":[{"output_type":"execute_result","data":{"text/plain":["{'?': 3,\n"," 'Br': 1,\n"," 'C': 4,\n"," 'Cl': 1,\n"," 'F': 1,\n"," 'H': 1,\n"," 'I': 1,\n"," 'N': 3,\n"," 'O': 2,\n"," 'P': 5,\n"," 'S': 6}"]},"metadata":{"tags":[]},"execution_count":8}]},{"cell_type":"code","metadata":{"id":"2N5zUFvSV7uv","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973619075,"user_tz":240,"elapsed":803139,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["def preprocess_smiles(smiles):\n","  return sf.encoder(smiles)  \n","\n","data['selfies'] = data['smiles'].apply(preprocess_smiles)"],"execution_count":9,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"rAriEcI7e5wl","colab_type":"text"},"source":["Let's take a look at some short SMILES strings and their corresponding SELFIES representations. We can see right away that there is a key difference in how the two representations deal with Rings and Branches. SELFIES is designed so that branch length and ring size are stored locally with the `Branch` and `Ring` identifiers, and the SELFIES grammar prevents invalid strings."]},{"cell_type":"code","metadata":{"id":"2dqSCmoPe30e","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":195},"executionInfo":{"status":"ok","timestamp":1600973619247,"user_tz":240,"elapsed":803291,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"aa45a65e-10f1-4241-e974-816f3d395a5a"},"source":["data['len'] = data['smiles'].apply(lambda x: len(x))\n","data.sort_values(by='len').head()"],"execution_count":10,"outputs":[{"output_type":"execute_result","data":{"text/html":["<div>\n","<style scoped>\n","    .dataframe tbody tr th:only-of-type {\n","        vertical-align: middle;\n","    }\n","\n","    .dataframe tbody tr th {\n","        vertical-align: top;\n","    }\n","\n","    .dataframe thead th {\n","        text-align: right;\n","    }\n","</style>\n","<table border=\"1\" class=\"dataframe\">\n","  <thead>\n","    <tr style=\"text-align: right;\">\n","      <th></th>\n","      <th>smiles</th>\n","      <th>selfies</th>\n","      <th>len</th>\n","    </tr>\n","  </thead>\n","  <tbody>\n","    <tr>\n","      <th>70607</th>\n","      <td>CCCC=O</td>\n","      <td>[C][C][C][C][=O]</td>\n","      <td>6</td>\n","    </tr>\n","    <tr>\n","      <th>99883</th>\n","      <td>CCOCCOC</td>\n","      <td>[C][C][O][C][C][O][C]</td>\n","      <td>7</td>\n","    </tr>\n","    <tr>\n","      <th>37561</th>\n","      <td>c1nnon1</td>\n","      <td>[C][N][=N][O][N][Expl=Ring1][Branch1_1]</td>\n","      <td>7</td>\n","    </tr>\n","    <tr>\n","      <th>73796</th>\n","      <td>COCCCCO</td>\n","      <td>[C][O][C][C][C][C][O]</td>\n","      <td>7</td>\n","    </tr>\n","    <tr>\n","      <th>92088</th>\n","      <td>CC#CCCCO</td>\n","      <td>[C][C][#C][C][C][C][O]</td>\n","      <td>8</td>\n","    </tr>\n","  </tbody>\n","</table>\n","</div>"],"text/plain":["         smiles                                  selfies  len\n","70607    CCCC=O                         [C][C][C][C][=O]    6\n","99883   CCOCCOC                    [C][C][O][C][C][O][C]    7\n","37561   c1nnon1  [C][N][=N][O][N][Expl=Ring1][Branch1_1]    7\n","73796   COCCCCO                    [C][O][C][C][C][C][O]    7\n","92088  CC#CCCCO                   [C][C][#C][C][C][C][O]    8"]},"metadata":{"tags":[]},"execution_count":10}]},{"cell_type":"markdown","metadata":{"id":"NrQelTLVa7wR","colab_type":"text"},"source":["To convert SELFIES to a one-hot encoded representation, we need to construct an `alphabet` of all the characters that occur in the list of SELFIES strings. We also have to know what the longest SELFIES string is, so that all the shorter SELFIES can be padded with `'[nop]'` to be equal length."]},{"cell_type":"code","metadata":{"id":"BkQ0Sd3TY3Aq","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973619421,"user_tz":240,"elapsed":803461,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["selfies_list = np.asanyarray(data.selfies)\n","selfies_alphabet = sf.get_alphabet_from_selfies(selfies_list)\n","selfies_alphabet.add('[nop]')  # Add the \"no operation\" symbol as a padding character\n","selfies_alphabet = list(sorted(selfies_alphabet))\n","largest_selfie_len = max(sf.len_selfies(s) for s in selfies_list)"],"execution_count":11,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"vQ2m_WoHt7_m","colab_type":"text"},"source":["`selfies` has a handy utility function to translate SELFIES strings into one-hot encoded vectors."]},{"cell_type":"code","metadata":{"id":"N9-d9yYMZSgI","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973619680,"user_tz":240,"elapsed":803715,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["onehots = sf.multiple_selfies_to_hot(selfies_list, largest_selfie_len, selfies_alphabet)"],"execution_count":12,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"daU67TZZbbLa","colab_type":"text"},"source":["Next, we \"dequantize\" the inputs by adding random noise from the interval `[0, 1)` to every input in the encodings. This allows the normalizing flow to operate on continuous inputs (rather than discrete), and the original inputs can easily be recovered by applying a floor function."]},{"cell_type":"code","metadata":{"id":"u3ThEWVcbvxn","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973623716,"user_tz":240,"elapsed":807747,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["input_tensor = tf.convert_to_tensor(onehots, dtype='float64')\n","noise_tensor = tf.random.uniform(shape=input_tensor.shape, minval=0, maxval=1, dtype='float64')\n","dequantized_data = tf.add(input_tensor, noise_tensor)"],"execution_count":13,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"B38gEbh6uLrr","colab_type":"text"},"source":["The dequantized data is ready to be processed as a DeepChem dataset and split into training, validation, and test sets. We'll also keep track of the SMILES strings for the training set so we can compare the training data to our generated molecules later on."]},{"cell_type":"code","metadata":{"id":"O3JqekV0HjNm","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":34},"executionInfo":{"status":"ok","timestamp":1600973623718,"user_tz":240,"elapsed":807719,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"28d49000-e9d0-456b-b17f-29aa8ec53d68"},"source":["ds = NumpyDataset(dequantized_data)  # Create a DeepChem dataset\n","splitter = RandomSplitter()\n","train, val, test = splitter.train_valid_test_split(dataset=ds, seed=42)\n","train_idx, val_idx, test_idx = splitter.split(dataset=ds, seed=42)\n","\n","dim = len(train.X[0])  # length of one-hot encoded vectors\n","train.X.shape  # 2000 samples, N-dimensional one-hot vectors that represent molecules"],"execution_count":14,"outputs":[{"output_type":"execute_result","data":{"text/plain":["(2000, 588)"]},"metadata":{"tags":[]},"execution_count":14}]},{"cell_type":"code","metadata":{"id":"9In8bdWddovm","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973623720,"user_tz":240,"elapsed":807714,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["# SMILES strings of training data\n","train_smiles = data['smiles'].iloc[train_idx].values"],"execution_count":15,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"yZmmABKzI00F","colab_type":"text"},"source":["Next we'll set up the normalizing flow model. The base distribution is a multivariate Normal distribution. The `permutation` layer permutes the dimensions of the input so that the normalizing flow layers will operate along multiple dimensions of the inputs. To understand why the permutation is needed, we need to know a bit about how the normalizing flow architecture works."]},{"cell_type":"code","metadata":{"id":"W_Ff2Q4rIyCe","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973623721,"user_tz":240,"elapsed":807709,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["base_dist = tfd.MultivariateNormalDiag(loc=np.zeros(dim), scale_diag=np.ones(dim))\n","\n","if dim % 2 == 0:\n","    permutation = tf.cast(np.concatenate((np.arange(dim / 2, dim), np.arange(0, dim / 2))),\n","                                  tf.int32)\n","else:\n","    permutation = tf.cast(np.concatenate((np.arange(dim / 2 + 1, dim), np.arange(0, dim / 2))), tf.int32)"],"execution_count":16,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"FMCyGvKKJwXw","colab_type":"text"},"source":["For this simple example, we'll set up a flow of repeating [Masked Autoregressive Flow](https://arxiv.org/abs/1705.07057) layers. The autoregressive property is enforced by using the [Masked Autoencoder for Distribution Estimation](https://arxiv.org/abs/1502.03509) architecture. The layers of the flow are a bijector, an invertible mapping between the base and target distributions.\n","\n","MAF takes the inputs from the base distribution and transforms them with a simple scale-and-shift (affine) operation, but crucially the scale-and-shift for each dimension of the output *depends on the previously generated dimensions of the output.* That independence of future dimensions preserves the *autoregressive* property and ensures that the normalizing flow is invertible. Now we can see that we need permutations to change the ordering of the inputs, or else the normalizing flow would only transform certain dimensions of the inputs.\n","\n","Batch Normalization layers can be added for additional stability in training, but may have strange effects on the outputs and require some input reshaping to work properly. Increasing `num_layers` and `hidden_units` can make more expressive flows capable of modeling more complex target distributions."]},{"cell_type":"code","metadata":{"id":"byIooYBqJ2UC","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973623723,"user_tz":240,"elapsed":807703,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["num_layers = 8\n","flow_layers = []\n","\n","Made = tfb.AutoregressiveNetwork(params=2,\n","                                 hidden_units=[512, 512], activation='relu')\n","\n","for i in range(num_layers):\n","    flow_layers.append(        \n","        (tfb.MaskedAutoregressiveFlow(shift_and_log_scale_fn=Made)\n","    ))\n","\n","    permutation = tf.cast(np.random.permutation(np.arange(0, dim)), tf.int32)\n","    \n","    flow_layers.append(tfb.Permute(permutation=permutation))\n","    \n","#     if (i + 1) % int(2) == 0:\n","#         flow_layers.append(tfb.BatchNormalization())"],"execution_count":17,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"KMbxkF_8KZxR","colab_type":"text"},"source":["We can draw samples from the untrained distribution, but for now they don't have any relation to the QM9 dataset distribution."]},{"cell_type":"code","metadata":{"id":"hBYNQrAYKQij","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":50},"executionInfo":{"status":"ok","timestamp":1600973659310,"user_tz":240,"elapsed":843260,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"4bae2bf9-6d54-47fa-86b3-1b839b52e9fb"},"source":["%%time\n","nf = NormalizingFlow(base_distribution=base_dist,\n","                    flow_layers=flow_layers)\n","samples = nf.flow.sample(5)"],"execution_count":18,"outputs":[{"output_type":"stream","text":["CPU times: user 45.7 s, sys: 1.77 s, total: 47.5 s\n","Wall time: 35.5 s\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"pa04f-1VcG0p","colab_type":"text"},"source":["A `NormalizingFlowModel` takes a `NormalizingFlow` and any parameters used by `deepchem.models.KerasModel`."]},{"cell_type":"code","metadata":{"id":"iA56ui2MK1QA","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973659311,"user_tz":240,"elapsed":843255,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["nfm = NormalizingFlowModel(nf, learning_rate=1e-4, batch_size=128)"],"execution_count":19,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"IL-Onju8K8nK","colab_type":"text"},"source":["Now to train the model! We'll try to minimize the negative log likelihood loss, which measures the likelihood that generated samples are drawn from the target distribution, i.e. as we train the model, it should get better at modeling the target distribution and it will generate samples that look like molecules from the QM9 dataset. "]},{"cell_type":"code","metadata":{"id":"ZrmHYIHGK7-l","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973659314,"user_tz":240,"elapsed":843253,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["losses = []\n","val_losses = []"],"execution_count":20,"outputs":[]},{"cell_type":"code","metadata":{"id":"vIURsPTpLZdh","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":608},"executionInfo":{"status":"ok","timestamp":1600973888187,"user_tz":240,"elapsed":1072102,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"592d9815-bd6e-457d-cc30-aff532b3b0ba"},"source":["%%time\n","max_epochs = 20 # maximum number of epochs of the training\n","\n","for epoch in range(max_epochs):\n","  loss = nfm.fit(train, nb_epoch=1, all_losses=losses)\n","  val_loss = nfm.create_nll(val.X)\n","  val_losses.append(val_loss.numpy())"],"execution_count":21,"outputs":[{"output_type":"stream","text":["WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","WARNING:tensorflow:Model was constructed with shape (None, 588) for input Tensor(\"input_1:0\", shape=(None, 588), dtype=float64), but it was called on an input with incompatible shape (1, 128, 588).\n","CPU times: user 7min 9s, sys: 9.26 s, total: 7min 18s\n","Wall time: 3min 48s\n"],"name":"stdout"}]},{"cell_type":"code","metadata":{"id":"k33LyZsPNwUg","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":265},"executionInfo":{"status":"ok","timestamp":1600973888192,"user_tz":240,"elapsed":1072090,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"5a7f48c7-aa41-48c5-fe95-78b78cb23048"},"source":["f, ax = plt.subplots()\n","ax.scatter(range(len(losses)), losses, label='train loss')\n","ax.scatter(range(len(val_losses)), val_losses, label='val loss')\n","plt.legend(loc='upper right');"],"execution_count":22,"outputs":[{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<Figure size 432x288 with 1 Axes>"]},"metadata":{"tags":[],"needs_background":"light"}}]},{"cell_type":"markdown","metadata":{"id":"9k-x3QVMOVNr","colab_type":"text"},"source":["The normalizing flow is learning a mapping between the multivariate Gaussian and the target distribution! We can see this by visualizing the loss on the validation set. We can now use `nfm.flow.sample()` to generate new QM9-like molecules and `nfm.flow.log_prob()` to evaluate the likelihood that a molecule was drawn from the underlying distribution."]},{"cell_type":"code","metadata":{"id":"mW8DeYFmOrJh","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946286,"user_tz":240,"elapsed":1130180,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["generated_samples = nfm.flow.sample(50)  # generative modeling\n","log_probs = nfm.flow.log_prob(generated_samples)  # probability density estimation"],"execution_count":23,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"s0M2xaqcdYEc","colab_type":"text"},"source":["Now we transform the generated samples back into SELFIES. We have to quantize the outputs and add padding characters to any one-hot encoding vector that has all zeros."]},{"cell_type":"code","metadata":{"id":"DVVQ-dwWdXWb","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946294,"user_tz":240,"elapsed":1130183,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["mols = tf.math.floor(generated_samples)  # quantize data\n","mols = tf.clip_by_value(mols, 0, 1)  # Set negative values to 0 and values > 1 to 1\n","mols_list = mols.numpy().tolist()\n","\n","# Add padding characters if needed\n","for mol in mols_list:\n","  for i in range(largest_selfie_len):\n","    row = mol[len(selfies_alphabet) * i: len(selfies_alphabet) * (i + 1)]\n","    if all(elem == 0 for elem in row):\n","      mol[len(selfies_alphabet) * (i+1) - 1] = 1"],"execution_count":24,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"tpwHYMP0LAvS","colab_type":"text"},"source":["`selfies` has another utility function to translate one-hot encoded representations back to SELFIES strings."]},{"cell_type":"code","metadata":{"id":"2XV-ZTgFjP04","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946296,"user_tz":240,"elapsed":1130158,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["mols = sf.multiple_hot_to_selfies(mols_list, largest_selfie_len, selfies_alphabet)"],"execution_count":25,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"hoC6RD8fdvVA","colab_type":"text"},"source":["We can use RDKit to find valid generated molecules. Some have unphysical valencies and should be discarded. If you've ever tried to generate valid SMILES strings, you'll notice right away that this model is doing much better than we would expect! Using SELFIES, 90\\% of the generated molecules are valid, even though our normalizing flow architecture doesn't know any rules that govern chemical validity."]},{"cell_type":"code","metadata":{"id":"F7EVnH9SdyN7","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":34},"executionInfo":{"status":"ok","timestamp":1600973946297,"user_tz":240,"elapsed":1130134,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"329baf39-cb2a-41d6-c468-9240a0349129"},"source":["from rdkit import RDLogger  \n","from rdkit import Chem\n","RDLogger.DisableLog('rdApp.*')  # suppress error messages\n","\n","valid_count = 0\n","valid_selfies, invalid_selfies = [], []\n","for idx, selfies in enumerate(mols):\n","  try:\n","    if Chem.MolFromSmiles(sf.decoder(mols[idx]), sanitize=True) is not None:\n","        valid_count += 1\n","        valid_selfies.append(selfies)\n","    else:\n","      invalid_selfies.append(selfies)\n","  except Exception:\n","    pass\n","print('%.2f' % (valid_count / len(mols)),  '% of generated samples are valid molecules.')"],"execution_count":26,"outputs":[{"output_type":"stream","text":["0.90 % of generated samples are valid molecules.\n"],"name":"stdout"}]},{"cell_type":"markdown","metadata":{"id":"pyt6ta2-d5Rd","colab_type":"text"},"source":["Let's take a look at some of the generated molecules! We'll borrow some helper functions from the [Modeling Solubility](https://github.com/deepchem/deepchem/blob/master/examples/tutorials/03_Modeling_Solubility.ipynb) tutorial to display molecules with RDKit."]},{"cell_type":"code","metadata":{"id":"XyE4CuaRe7BL","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946300,"user_tz":240,"elapsed":1130119,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["gen_mols = [Chem.MolFromSmiles(sf.decoder(vs)) for vs in valid_selfies]"],"execution_count":29,"outputs":[]},{"cell_type":"code","metadata":{"id":"JehQTBLXd9Gn","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946301,"user_tz":240,"elapsed":1130113,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["def display_images(filenames):\n","    \"\"\"Helper to pretty-print images.\"\"\"\n","    for file in filenames:\n","      display(Image(file))\n","\n","def mols_to_pngs(mols, basename=\"generated_mol\"):\n","    \"\"\"Helper to write RDKit mols to png files.\"\"\"\n","    filenames = []\n","    for i, mol in enumerate(mols):\n","        filename = \"%s%d.png\" % (basename, i)\n","        Draw.MolToFile(mol, filename)\n","        filenames.append(filename)\n","    return filenames"],"execution_count":30,"outputs":[]},{"cell_type":"code","metadata":{"id":"oyWxxxqvnKGf","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":1000},"executionInfo":{"status":"ok","timestamp":1600973946557,"user_tz":240,"elapsed":1130349,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"8e039e2f-ebe2-4143-cde8-f1303fe3ba71"},"source":["display_mols = []\n","for i in range(10):\n","  display_mols.append(gen_mols[i])\n","\n","display_images(mols_to_pngs(display_mols))"],"execution_count":31,"outputs":[{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAABmJLR0QA/wD/AP+gvaeTAAAWvElEQVR4nO3de1BU5/3H8e/ZAwssgiKIIBiVZkpCvGApJnhDdCWltdrakNZbMu1MMJOYxomxphpltE2CN6KBmGDaIErjLxRtYi6msgmiVk0DMUZMRUsaE+SiKygXIcju8/vjMMcVF9hddvku8HlN/+FkeXiY+ubcz5GEEAQAfDTcEwAY6BAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4TgEnV1dUII7ln0DYgQXOI3v/lNSEjIww8/vHPnzoqKCu7puDUJf67A6YQQkZGRFy5cUL6UJGncuHGJiYmzZ8+eNm2aj48P7/TcDSIEV/n6668NBoPBYPjnP/9ZX1+vLPTw8Lj//vt//vOf6/X6iRMnajTYFkOE7uraNQoIoIYGGjSIiCgoiAwGio7mnpYNjh07NnXqVMslbW1tp0+fVoI8fPhwW1ubsnzYsGEzZszQ6/VJSUkjR47kmKx7EOCW6uoEkWhoaP8yMFCcOsU6Idts375dkqSVK1d29gGj0ZiXl5eSkjJq1CjLf4cREREpKSl5eXnXr1/vzQm7A0TopvpihC+//LIkSZIkZWZm2vL58vLyrKys5OTkwYMHqzV6eHjExMSkpqYWFxebTCZXz9kdIEI31eciTE9PJyJJkl544YXa2lq7vretra24uDgtLU2v13t6eqpBBgUFJScnZ2VlffPNNy6atjvAPqGbUvYJg4NJkoiILl+mzz93333C9PT0FStWKOvAzz77bM+ePdHR0Xq9Xq/XT58+XavV2j5UY2NjYWHh+++/X1BQ8L///U9dHhERoQyYmJhouebsD7j/CoB1ypqwvFxUVYmqKjF0qPuuCbds2UJEkiTt2LFDCLFgwQLL6vz8/ObOnZuRkVFWVmbvyOfPn8/MzJw3b56/v786oFarLSwsdP6vwQcRuqm+sjm6efNmpcDXXntNXdjU1FRQULBq1aqYmBhJWZUTEVFISEhycnJOTo7RaLTrp1hur3p7e1+9etXZvwcnROim+kSEmzZtIiKNRpOdnd3ZZ6qrq5XDoWFhYWqNGo0mJiZm1apVBQUFLS0tdv3QxsbG77777sEHH/T29r777rvz8/OV5U8++eSyZcvefffd+vr6nvxSvQ8Ruin3j3Djxo1EJMvyrl27bPyW8vLybdu2KWszNUidTqfX69PS0oqLi81msy3jTJ8+fdGiRZcvX/7kk0+GDBly9uzZ1tZWdZNVlmW18NbW1h78ir0EEYIj0tLSlH/uOTk5Dnz7jRs3rG6vDh8+XDkceunSpc6+9+LFi5IkVVVVKV/u37//4sWLJpPp+PHj69evnzJlioeHhzpgQEDAr371q9dff/3rr7928Fd1PUQIdlML3L17d89Hq6mpUbZXw8PDLQ8ZRkVFKWuz5uZmy88XFhaOGDGiiwEbGxvVwi0HVK8HsPcMiqshQrBPamqqUuCePXucPrh6+t7Pz0+Nx8fHx3J7df/+/ZGRkTYOWFlZmZOTk5ycPHToUHVAy+3V77//3um/hb0QYZ/hgn/zdnNpgZZu3Lhx6NChlStXTpgwwXJ7NSsrq6ioKDQ01N4BTSaTenzVy8tLHdDX11ct3BW/iC0QYZ+xaBHzBNatW6cUmJub25s/9/Lly8r26siRI8vKympqajw9PSsqKpT/unv37vfee8+uATs7gxIaGrpkyZK8vLxePgWCK2b6gPx8ys+nkyfpgQcoLIy2bmWYw7p16/70pz8p+4ELFy5kmIGF5ORkjUazY8eOixcvzpkzJz4+3mg0KtfT/OhHP7LsqlvV1dVHjx41GAzvv/9+ZWWlslCWZYev+HFEbxYP3VLOTGzb1v7lhAm3zkwwrgmff/555Z/mW2+9xTYJC0ajcf78+TqdLiIiIj8/f8yYMeq/55CQkMWLF+/evVs9fGojZXv1xRdfTEhIsNxeVa74uXDhgot+F4HNUXdTVyd8fUVUVPuX7hDhmjVriMjT03Pfvn08M+jOlStXur49yt7T9x22VzUazeXLl100eYEI3U1dnRgxQkybJo4cEeL2CFVNTaLXTnqtXr1aKXD//v299CN7prPbo6ZMmaIcfbH39qiKiop//OMfLpqtAhG6l7o6ERQkcnPb13tWI9y/XxCJiAiRkiLy8oTrTnr98Y9/JCKtVuvqf4WucPPmTfVwqOXpe/X2qIsXL3LPsR0idC91dSIwULS0iLAwYTRaj3DnTjF0qCBq/5+Hh5g8WaSmin/9S9y86bSZPPfcc0qB77zzjtMGZaLezj969Gir26td387/n//8p0G9gNAFEKF7USIUQjzzjNiyRURHW79k1GQSxcUiLU3o9cLL61aQvr5CrxdpaaInJ73MZvPTTz+tFPjuu+86PpBbUrdXhwwZYrm9qp6+v3n7X7L58+cTkUv3hxGhe1EjPHdOREaK2Njur9tuaBAHDoinnhL33HOrRiIxZox4/HFzfv4+u67SMpvNv//97/trgZY6u50/MDDQ8nb+DRs2ENETTzzhupkgQjeyd6+oqGiPUAgxY4bw97fv5omqKpGXJ1JSxIgRgkhERTWSPVdpmc3mp556SinwwIEDPfhV+pja2tr8/PylS5danu0gonvuuSczM5OIbL9QzgGI0F1s2SKIxNSptyLcu1cQOXgHk7K9mpl5xupZr4yMjHPnznX4FrPZvGzZMiLy8vIaUAV2oG6vBgQEENHp06eVA63ffvuti34iInQLSoGSJHbscP7gXd/nnpWVVVlZaTabn3zySaVAe68C669u3rx5/Phxs9k8d+5cIurixuUeQoT8Nm9uL9DiARGuUlFRkZ2dvXDhwuDgYLVGjUaj3Pbu4+Nz6NAhl0+ir9m+fTsRLV682EXjI0JmmzYJIqHRCJf9ne2Ust01Z84cb2/v4ODg2NjYgoKC3p5EX1BaWkpEw4cPt/HGf3vhAm5OmzfTH/5Askx//Ss9+ijbNJqbm//73/+OHTvWrkufB5Tw8PBLly6Vlpbed999Th8cr+Ngs2lTe4FvvtlpgUII5Z4dl87Ex8dn3LhxKLALCQkJRPTxxx+7YvABE2FZGcXFkbc3jR9Pn37KPRvauJFWrSJZpuxseuQR658xmUyzZs0aOXJkfHy8+hIVYDFr1ixyWYQDY5/QbBZjx4rly8XVq2LrVhESIm7cYJxOWpogErLczc3yX375pfp/U0lJSW/NDqxQ3nPq5+fnise3DYwIS0qEl9et5wfedZfgux4yNdWmAoUQzc3Nd999NxGNHj26qampV2YHnYqMjCSi48ePO33kgbE5eu4c/eAH7W/6I6Lx4+mrr+jKFer1bbzUVFq/nmSZcnJo8eJuPuzt7V1SUmIwGL744gudTtcrE4RO6fV6IjIYDE4feWBE2NxMFk/vIn9/am6mpUtpyBCaPZs2bqSSkl6Yxbp1tGEDyTLt3k2LFtn0Lf7+/rNmzepv7z/pm1y3W+jR/Uf6gWHD6Pr1W1/W11NwMFVWUlMTGQyk/G0bM4YSE2n2bJo5kwICnD6FtWvpz38mWaY9e2jBAqcPDy6XkJAgy/KJEyeampp8fX2dObTTN3DdTkmJKC0VWq1Qn3Fw113iww+FsLjeOTT01t0HsixiYsSqVaKgQDjpoZRr1ggi4ekp3PUBEWCT2NhYIjp48KBzh+3vEe7cKTQasWGDmDBBLF8url8Xr7wiwsNFh5eQmEzis8/Eiy+KhASh1d4K0s9PzJ174I3qO652tsPq1e0F9pEHRECnlEcNPPvss84dtl9HqBQoSWLbNnH+vIiLE15eYsKEbu54bWoSBQVi1SoREyMkyTzIT+fZSiRCQkRyssjKEpWVdkxhxQpBJLRaxsOx4DTKUZmJEyc6d1jHIzx58uTJkyedOBUny8oSkiQkSWzf7vggFRWX9xoWLhTBwbfWjhqNiI0Vq1eLwsLuN1c3bRJareiDj2gBK5qbm318fCRJcu7D1xyMMCMjQ9mlfPXVV504G6d5/fX2Al95xVlDlpeLrCwxZ47w9r4VpE5363ESZrP1p4aeP++sKQA/5UTF22+/7cQxHYzwF7/4hRLh/PnznTgb53jttfYCMzJcMfyNG+Kjj8SKFWL8eCFJt4KcPLmrp4ZC//DSSy8RUUpKihPHdPA84WOPPabT6XQ63WOPPdbjA7ROtW0bPfEEEVFGBi1b5oqf4ONDDz5IW7bQ6dNUXU15eZSSQuHhpFxeP3gwBQbS0aOu+MnATzlb6ORT9g7n29DQ4NLnwDkiPb399the30g2m0VDg01PDYU+ra2tTXnLmhPfOur4FTODBg0apF4I5g7S0+mZZ0iSKDOzfWXYiySp/ao4Ieihh+jwYbp6tZenAL1BluUZM2aQU1eG/eWyta1bacUKkiR69dXeL7ADLy/69a9p1y7CDXr9ktOvX+sXl61t2UIrV5Ik0Y4d9Pjj3LMhIkpJoXnzyN+fex7gAsoB0o8//thsNms0TliN2THEtWskSbR9e/uX0dH0xRc9n0BP/d8rr7SsXUsaDWVnu0mBRBQZSaGhVFbGPQ9wgR/+8IejRo0yGo2WN3z2hH0d+/rSzp0dF/7lL/STn9CSJU6Zj31eeOGFBU8/nTx+PGVncz6kxZqlS6m+nnsS4BozZ84kJ26R2n4Mx5a3dvWmtLQ0IpJlOScnh3MeMPDk5uYSUVJSklNGs29N2NpKS5dSVlanH0hLo8xMOn++Z38YbLBx48bnnntOluXs7OxHOntIS69TttgbG9u/DApyiy12cDq9Xi9JUlFR0ffff++E4Wzvtdu3drW1iYCA9stHlMudc3KE0eiUPxa3SU1NJSJZlvd0+4iI3qVctqaePQ0MxHnCfmvs2LFEVFRU1POh7D6208Xxd5OJ0tNpwQIKDqbqavr73+nRRyk4mCZNojVrqKiIWlud8FcjNTV1/fr1sizv2rVrcbePiABwDWeeqLC9V7ve2lVeLrZtE3p9V5c7O2DdunVEJMtybm6uI9/vYlgTDhwHDhwgosmTJ/d8KEciFPa8taupSXz0kXjmGTFu3G1vz5s798Pf/e53e/futf2ukLVr1yoF/u1vf7N92r1JiTA4WAwfLoYPF5KECPut+vp6T09PDw+Prt/yawsHI3TsrV01Ne1PkwgPFxERP1PXxlFRUcrb85qbmzv73ueff14p8K233rLvp/YiJcLyclFVJaqqxNChiLA/e+SRR6ZOnfrBBx/0cByeO+vNZnH69OnNmzcnJib6+PioNep0uqSkpPT09DNnzlh+fs2aNUTk6enp0rcW9xw2RwcUZedo+fLlPRyH/4UwLS0tx44dMxgMBoPh1KlTZrNZWR4cHBwfH6/X60tLSzMyMjw9Pd9+++1f/vKXvLPt2rVrFBBADQ3tF3MHBZHBQNHR3NMC1zhy5Eh8fPzYsWPPnDnTk3H4I7R05cqVw4cPGwyGgwcPfvfdd8pCf3//lpaWvLy8efPm8U6vW4hwQGltbQ0MDGxqarp06VJoaKi63Gw2nzp1qrW1NS4uzpZx3CtCS6WlpYcOHSooKFiyZElAQEBSUhL3jAA6+ulPf3rw4MHc3NxFFo9z3rdv30MPPTRz5kwbT2C4b4QA7m/r1q3PPvvsb3/72zfffFNdePXq1eDgYK1WW1tba3nIozP95X5CAA7KKftDhw5ZLgwMDIyOjlYOdtgyCCIEsEltbW1qauoTt98yPmHChGHDhl26dOn87RdMq/cc2jIyIgSwiaen50svvfTGG2/UW9yiJkmS1dua7LqoDREC2MTPzy82Nratre3IkSOWy632Nm3aNG9v788//9xoNHY7MiIEsJXV3hITE5WFJpNJXejj4xMXF2c2m4uKirodFhEC2MrqQ0dHjRoVERFx7dq1U6dO3flhW7ZIESGAreLi4nx9fc+ePVtVVWW53OpLfG1/TDAiBLCVVqudNm2aEKKwsNByudWVXmxsbEBAwIULF7755puuh0WEAHaw2tusWbM0Gs2xY8eam5vVhbIsT58+nYg++eSTrsdEhAB2sLrlGRgYOH78+JaWluPHj1sut3G3EBEC2EE5O//tt99euHDBcrnVs/Pqwq4vDkWEAHZQz85bPQzTIcJ77703PDy8pqbm7NmzXYyJCAHsY7W36dOne3l5lZSU1NbWWi63WmwHiBDAPrNnz6Y7zs7rdLoHHnjAZDJ1ODtvy24hIgSwz+jRo8eMGWPj2Xllt7CoqOjmzZudDYgIAezWxWGYDlueI0aMuPfeexsaGv797393NhoiBLBbZ2fnBw8eXFZWpj6ZxfLDXewWIkIAu+n1eo1Gc/ToUcuz8x4eHsrZeXtva0KEAHZTz86fOHHCcrnV3hISEmRZ/vTTTxvVVwXdDhECOKLr3ULLs/ODBw/+8Y9/3NraevToUatDIUIAR1jd07vvvvvCwsKqq6u/+uory+VdP+0CEQI4Qj07X1dXZ7k8ISGBOtkt7OzYDCIEcIROp7v//vtNJtPhw4ctl1vdLZwyZYqvr++XX35ZU1Nz51CIEMBBVntTrqcpLCy0PDuv1WqnTJly542ICkQI4CCre3phYWGRkZENDQ3FxcWWy7s4UYEIARw0adIkf3//c+fO2XJ2XllYUFBw5ziIEMBBHh4e8fHxdMe981ZXehMnTgwMDKyoqKioqOgwDiIEcJzV3mbOnCnL8okTJ5qamtSFGo3mww8/vHLlSnh4eIdBECGA45TdwoKCAsuz80OGDImJiRk6dGh5ebnlhydNmhQQEHDnIHgrE4DjhBBhYWFVVVVnz56NiopSlxuNxqCgIBsH8XDN3AAGBEmSkpKSysvLO1wXanuBhDUhADvsEwIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhADNECMAMEQIwQ4QAzBAhALP/B4TEqHR9xbfMAAAAAElFTkSuQmCC\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAABmJLR0QA/wD/AP+gvaeTAAAQpUlEQVR4nO3dfWzTdR7A8U/pxoTBxoO4TTELiwiObQ66tbBug0RA5RA5z9wlnBK5O/XwkIMY1Jx3wSCcOYPx6USn+MfMzeNMAAGdBB8S9oBrYVvHgzxNJAwmIjCoIA8b9P741bbMMcvsfp+tfb9CSNNf136Vvfv7tv3197X4fD4BoKeP9gCAWEeEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI0JAGRECyogQUEaEgDIiBJQRIaCMCAFlRAgoI8IYNW+eWCxy+HDwmo0bxWKRN9/UG1OsIkJAGRECyogQUBanPQBoOnxY2tr8l7/9VnUoMYwIY9qECdojABHGuJUrZehQ/+X6elmyRHU0sYoIY9qdd8rw4f7L112nOpQYxhszgDIiBJQRIaCMCAFlFp/Ppz0GIKaxJwSUESGgjAgBZUQIKCNCtLdvn9TVaQ8ilnDYGtrbtEkOHpRx47THETPYEwLKiDCGGOeV+cMfgtccPhw8r4zbLampkpoqf/ubvPGG//K6dVqDjSFEGHPKyqS5uYPr7XY5elSOHpV//lPmzvVfvvde08cXe4gwtqSny4AB8vLL2uNACCKMLRcvymOPSUmJeL3aQ8GPiPCXamxsfO+9944ePao9kPYuXJCaGnn1Vfn97+U3v/Ff2doqjz8uFy9KSclVf3DePFm+3JwxQoSPKLrA6/Vu3769urq6qqrK7XYfO3ZMRN59990HH3xQe2jS3Cy1tVJdLVVVUlsr58/7r4+Pl3PnRER8PrnhBpk9W155Rf76V8WRIogIf15ra2t9fb3b7Xa73S6Xa//+/aFfPUlNTXU4HCkpKSpjO3lSXC5xu/1/nzgR3GS1SlaWOBz+PwkJwU1PPCErV0pZmUyZYv6Q0R4Rdqy5ubm2ttbY3dXV1Z0z9iMiIhIfH5+Tk+N0Om02m81mGzNmjJkDa2uTvXuD+7rduyX0u2hpaWKz+f84nTJkSMd3cuutMmOGLF8ukyebM2p0hgj9OpxkBmRkZASqy8/PTwjdrXS/xsbGrVtH1NRY3W6pr5cLF4KbEhNl3Ljg7u7mm8O9z0WLxOmUTZu6Y7y4NrEbYVtb2969ewO7uz179ly+fDmwNTU1NS8vz6iuoKBgaODEgKb46TPCbbed3b27v7E1I0OcTv/uzm6Xvn278hAFBVJQICtXisUSyZGjC2Irws4nmWPHjg3s7jIzMy0m/nq2trZ6PB6Xy3W1l52TJ3/1wAPZ48dLXp4kJUXmQRctkl//OjJ3hV8iyiP8/vvvGxoajPA2b97coyaZ6i87Z8yQUaNk796r3uDsWfnPf+TRR7vjwREUteeYWbBgwcaNG/ft2xf6H5iSkmK32x0Oh8PhyM/PT05ONnNIxiSzZz4j/JTPJ5MmSUWFLF0qzzyjO5YoF7V7wv379+/du7fdLsX8SWZDQ4PL5Tp06NC6devaPSOkpqba7Xa73T5+/Pj8/PykSM0yI8RikSeekOpq+cc/JDVV/vhH7QFFr6jdE27btq1Pnz45OTlxcaY+0XQ4ySwqKqqsrNR9RuiakhL585/FapX335f77tMeTZSK2ghNc+rUKePdFOPv7777LrCpT58+o0aNcjgcRUVFY8eOzc7ONvkZISIWL5YlS6RfP9m0SQoLtUcTjYjwmnX+2cagQYPy8vKM3Z35n210k/nz5bXXJDlZKiokJ0d7NFGHCMNiTDKN8LZs2fLDDz8ENvXGSea1unRJfvc7Wb1abrpJqqslPV17QNGFCDsW+tlGRUXFt1cuY5uWllZYWGiE1xPeyTTB+fMydapUVkpmplRWXvWAuF7N6/WuW7duyJAh06ZNM/OZlAj9ApNMI7z6+vrQSWZycnJ+fr5R3YQJE66//nrFoUbW22+/vWHDhokTJy5cuLBPn86+2nb6tEyaJB6POBzy2WeSmGjaGM3Q2tpqs9l27NghIosWLXrhhRdMe+iYjjDGJ5kisnbt2vt+fNNzxYoVc+fO7fz2zc3idMrBg/KrX8kHH0gvfJvpqrZu3Wq3243LaWlpzR2eAqR7RNH/xTCETjIrKyvbfRM3dJKZl5d3XQwsXVtfXx+43NDQ8LO3v/FG+eQTcTrlo49kzhx5993oOfR0xIgRiYmJZ8+eFZGsrCwzHzrK94SXLl3as2dPDE4yw1RXV1dQUHDhwgWr1VpeXj516tRwfsrtljvukDNn5JlnZOnS7h6jeSoqKl588cWhQ4cuW7YsLS3NtMeNwgg7n2SOHDkysLuL1knmNdm1a9cnn3xSWFiYl5cX/k+Vl8u990pbm7z0kixY0H2jiwnREKHX6/V4PMa3flwuV+gk02KxjBw50uFwGIeM5ubmxsfHKw41mpSVyYMPisUi//2v/Pa32qPpzXplhD+dZBYVFW3evNnYmpycnJWVZezuxo8fP2zYMDPH5vV6m5qaTP66vZZ//Uueflr69pUNGyS8mSw60GsibGpqcv2orq7OeAFtSEhImD59+k033WTs8W655RYzB/bTZ4T09PQDBw6YOQZFCxbIK69IUpJs3uzLze01c3ufT/bskVGjpNMPZa7Nvn1y5kxX1vDoue+OnjlzxuPxGL/cVVVVX3/9dejWtLQ0m81m7O7Mfyfz0KFDbre7pqbG7XbX1taGvuxMSEhISUk5d+5cv379zBySlpdekpYW+eKLHQ88MHvNmv/deuut2iO6qtOnZetW/7l5vvhCTpyQXbskMzNi99/lhXR6UITtdikej+fSpUuBrUlJSdnZ2VqTzNBnhMrKyoMHD4ZujcHPNgIsFnn7bbn//sUbNnimT59eVVV1ww03aA/K7/x5qavzn4rO5ZIrn8bl5pvl6NFIRth1PlVHjhxZv3794sWLp0+fPmjQoNCBxcXFZWZmPvLII6WlpTt37rx8+bKZA2tra9u5c2dpaen8+fNtNlu7Q0mSkpKcTudTTz21fv36Y8eOmTmwnsnr9dpsNhGx2Wxer1dxJEeO+N5/3zd/vs/p9CUk+ESCfwYM8DmdvvnzfaWlvgMHgj/yl7/4RHxz5gSvaWryifjeeOOKGzQ1BW/w8cfBG7hcvpQUX0qKb+BAX//+/ssffHANYzZ7Txj+JNNms5k8owt8tmGM7dSpU4FNcXFxo0eP5rONqxk4cODHH39cVFRUW1s7c+bM8vJy046nDZ1k1tTI8ePBTVarZGb6z4hVWCi5uWK1XvV+yspk6VK58cZrHoCxkI6I/PvfcvBgV05e3u0Rhk4ya2tr3W53a2trYGvoJNPhcJg8k+nJLzt7nWHDhpWXlzudzs8///yhhx4qKyvr/EjULjt//nxDg9TUXGdMMtu9BTZ8uNjtMn682O2SlxfuAa7p6fL99/Lyy2LiEaNB3RJh6C6lurq6paUl+HhxcZmZmYFf7ttuu62b/qk6FP4zgvkvO6NARkbGpk2biouLV61aNWTIkNdffz1S99zc3Gx8e9P4h7Pbt1VU+D8ESkyU3Fz/7q6oSEaM6Mr9G+vkvPqq/P3vETuZXfgiE+HZs2fr6+sDv9xffvll6FbdSaaIfPXVV2+99ZbL5aqtrT1z5kzg+r59+xof4ht/jxw50uSBRZ/s7Oy1a9feddddK1asSE9Pf/LJJ7t2P8ePHw89X0Ho87jVak1NrfvTn8YY5zvOzOxskhkmY52c5culpEQWLer4NocPS1ub//KV32zzmzevi4/exQjb7VK2bt168eLFwNaBAwfm5OQY4U2cOFH97bKWlpbAN1PUnxGi3qRJk1atWnX//fc//fTTw4YNmzNnTjg/ZXyVLLC72717ty/kE2zjX81QWFg4ePDgyI45nHVyJkyI7GMGXUOE33zzzbZt23rgJPNn3X777YsXLzZObRaDR2mbb+bMma+99tpjjz328MMPDx48eObMmR3erN0k83xgESmRxMTE3NzcQHjmHIHU+To5K1dK4Fwl9fWyZEnEHjfcCJcvX77oyv30iBEjjBN42u32cePG9eT3LeLj45999lntUcSWuXPnNjU1Pf/887Nmzfr0008LCgpE5PTp01u2bAlMMk+ePBm4vdVqzcrKcvxozJgx1l8+y7xGna+Tc+edMny4/3Jkf9nDjTA7Ozt0kllcXKy1GBh6i2XLlh07duydd96ZMWNGVVXV6NGjy8vLZ82aFbhBd08yO3f8uP9z/NBvlaqskxNuhJMnTz59+jQfjiF8FoulpKTk5MmTa9eunTJlypYtWyZMmFBcXGyc79hut98c/iJSkXC1VeVstuBtVNbJCTdC8+cGiAJWq7WsrGzq1KlVVVV33313ZWVl4Msu5ti/P7iIqscjIe8eyoABYrOJwyGNjRJ6GKL56+T0oGNHEZX69ev34YcfFhcXb9++fdq0aZ999ln//v277+G8Xtm+3b+7c7kk5FTMVxxAE7qqXLuPFn52nZyI6zVfZUKvduTIkYKCgkOHDt1zzz1r1qyJ4JnIw1+6uLBQzH3VGS4ihEl27dpVVFTU0tJSWlo6e/bsX3JXjY2NLpfrwIH0jz4q9HjaL11s7OWMI9fMfdXZRUQI81RVVW3cuPG555671nf4QpcuDiz4kZ396I4db0qEli5WRIToiS5evOjxeAJHru3bty90a1pamt1udzrvsNkej+DSxVp4YwY9hfrSxVqIEGpCJ5lut7uHL13cfYgQZjtx4sTChQuNSabvJ0sXBxYz72lLF3cfXhPCbK2trcnJyefOnYuRBT9+FhFCwerVqzMyMnrp0sURR4SAsh70rT8gNhEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLKiBBQRoSAMiIElBEhoIwIAWVECCgjQkAZEQLK/g8ky7Ttlj5YaAAAAABJRU5ErkJggg==\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAABmJLR0QA/wD/AP+gvaeTAAAJ/0lEQVR4nO3df2hVBR/H8c/d3MbEObdyk9KaIoW6tgIh0Db6oRgOY2ij/giGlf14LKhmMKMwR1HqrMwgnVkiQYUyqWSQP2AR/vhjQYomGDUrJRc1mcHcnO4+f3ge76PPNq967/mcR98v/OPs3nPv+Qp7c88999yzWDweFwCfDPcAwPWOCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAjAgBMyIEzIgQMCNCwIwIATMiBMyIEDAjQsCMCAEzIgTMiBAwI0LAbJh7AOAC3d3d7e3tsVhs4sSJ2dnZ7nHCwCshomLv3r2zZ8/Oz88vLS2dMmVKfn5+TU3NwYMH3XOlXSwej7tnALR8+fLFixf39/dfdHt2dnZTU1Ntba1lqnAQIfw2bNgwf/78we7NyMhoaWmZNWtWmCOFiQhh1tXVNX78+BMnTgyxTklJyeHDh7OyskKbKky8J4TZpk2bhi5Q0pEjR7Zv3x7OPOEjQpjt3r07mdX27NmT7klciBBmnZ2dyazW1dWV7klciBBmo0ePTma1wsLCdE/iQoQwq6ysTGa1ioqKdE/iwtFRmHV3d0+YMKGjo2OIdSZNmnTgwIGMjGvzNePa/F/h/8jw4cPXrFkTi8UGWyE7O3vdunXXaoEiQkRBdXX1+vXrc3Jy/veuvLy8zZs3T58+PfypQkOEiIT58+fv27evtra2oKDg3C3FxcULFy48ePDgnDlzvLOlG+8JETmdnZ0ZGRmjRo1yDxISIgTM2B0FrlZjo2IxxWL64IMreTgRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGWfMAMmaMUM7d17JA6uqtHXroPfySgiYcRl8IFmFhSouHuD27m79848k5eVp+PABVvjPN0MGxu4ocLUaG/Xyy5K0erWee+6yH87uKGxKS4PznmMxDX197fr6YLXS0qGeZ+zYS2xxzJihnseFCBEJ27Zp/373ECZEiKhYudI9gQkRIio++0zHjrmHcEj26Oj+/fubm5vTOso1YOTIZ06eHOOeItJycrR48cU3FhToxAn19en997VsmWMsq2Qj3Ldv39KlS9M6yjVg6tR/tbW5h4i2ESMGiLCmRk1NkrR2rV59VXl54c/lxO4o/CoqNH68JHV16aOP3NOEjgjhd/KkXnghWF61SmfOWKe5fIsWKR5XPH4lHxKKCBEF3d16/HGdu8Thr79q82b3QOFK9j1heXn5kiVL0jrKNWDkyP6qKvcQ0TbQVbbV26sRI/T008FRmcZGPfpoyHM5JRthWVlZWVlZWkfBdevcqZPPP6933lFfn77/Xq2tuvde81ShYXcUUXHzzYkXwMbGK3mGY8cS58EN+G/IP/1kQ4SIkLq6YKGlRYcOWUcJEREiQsrL9cADkhSPX0dnsfF9QkRLXV3w7fVPP9Wbbw78/b3BFBXpm2+GWmHGDP3991WNlw5EiGh58EFNnqwff1Rvr1av1htvXMZjs7J0551DrTAskr/v7I4iWmIxvfRSsPzhh+ru9ozR0aFlyzRzpsaOVW6ucnN100267z698ooOHEjxtogQkfPYY8FeaGenPvlEksL8U9lnz2rJEt16q+rrtWOHjh1TT496evTHH2pt1Vtv6Y479NBDOno0ZVskQkROTo4WLgyW331X/f3Kygpp02fPau5cNTSotzdx47Bhysy8YLWvv9a0aWpvT81GiRBR9Oyzys2VpJ9/1pYtwXIIXn9dX30VLOfna8UKtberr09nzui337R2rcaNC+79/XfV1qZmo0SIKLrxxsSveGPjJa5WlipHj2rFimC5uFhtbVq0SCUlwS3jxumpp/TDD5o8Objlu+/U2pqC7RIhIurFFxWLSdLevfrppzC2uHFjYi901SpNnDjAOoWFeu+9xI9DXNI3eUSIiLrtNs2ZEyx//nkYWzx/de2CAs2bN+hq99+f2D3+5ZcUbDeSn5sAkqS6uuAdWjjXnqmrU3W1Ojo0atRQnyhmZio/X6dOSVJPTwq2S4SIrspKTZ2q0K4YMnt2Uqv19OjPP4Pl88dprga7o4i086d0R0dzs/r7g+XKyhQ8IREi0h5+WLfc4h7iv5w6pYaGYLmwUHPnpuA5+VsUwGV44gl9/HGw3NSkBQtS8Jy8EgLJeu21RIFVVXryydQ8La+EQFLq6xMXJi4t1a5dGjkyNc/M0VHgEk6f1oIF2rgx+HHKFO3cmbICRYTA0P76S9XV2rUr+LGiQlu26IYbUrkJ3hMCgzp0SHffnSiwtlY7dqS4QBEhMJgdOzRtWnBiWmamVq7Uhg3Kzk79htgdBQbQ3KxHHgkuyF9QoC++0MyZ6doWR0eBi335pWpq1NcnSRMmqKVFt9+exs0RIXCBrVs1b55On5ak8nJt26aiovRukQiBhLY23XNP8K3CsjK1tobxfWIiBAJdXbrrruDKMaNHq60tpNNWOToKBBoaEtduevvt8E4c55UQkKTjx1VSkri8RVFRcHGNS/r226s9bMNHFIAkHT9+wWUOz39t95LOHUS9GuyOAmbsjgJmvBICZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmZECJgRIWBGhIAZEQJmRAiYESFgRoSAGRECZkQImBEhYEaEgBkRAmb/Boxn28bYOvj0AAAAAElFTkSuQmCC\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"iVBORw0KGgoAAAANSUhEUgAAASwAAAEsCAIAAAD2HxkiAAAABmJLR0QA/wD/AP+gvaeTAAAQSElEQVR4nO3deWyUZR7A8ad3pxf0slzlEgouhEuqHIkooqA2LsaUSNhm/9DMgiQdSNYMaJZJZFcnZs0WNFkHEggmahyRjcUjcVYWUpFLudJw1AK2lBZaDmkLlJaZZ/94m2kpBTrnb6Z8P+EPMp155+nwfNt33veZlxittQIgJ1Z6AMCDjggBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESGglFKNjY3PP/98Xl6exWLxeDzhfOoYrXU4nw+ITMuWLfvoo4+MvzudzuLi4rA9df/8TdjW1uZ2u6VHgWjS1NTU69/DoL9FWFdXZ7FYxo4d++STT1oslmvXrkmPCNFhxYoVqampSqkxY8YsWrQonE/df3ZHz58//+67727YsKGtrS02NlYp5fF4xo4du3Hjxjlz5kiPDlGgqanpxIkThYWFycnJYX1iHf0uXrxotVpTUlKUUjExMUVFRUeOHDl06NC0adOMW0pKSi5duiQ9TKB30R1hc3Oz3W4fMGCA8QNl3rx5Bw8e9H61vb3dbrcnJSUppQYPHvzll18KDhW4m2iNsKWlxW63Dxw40JvfgQMHer1nVVWVd3e0uLj4woULYR5qRGlpaamrq6uvr5ceSD9UXa337fPngdEXYWtra1lZ2UMPPWR0NXv27F27dt37IR6Px+FwpKenK6UyMzMdDkd4hhpR2traHA7HoEGDZs2alZiYaLVa29rapAfVr2zcqP/yF38eGE0ReqeRN78ffvih7w8/c+bMs88+azz2ueeeq6mpCd1QI0pbW9v69esHDx5sfO/Dhw+PiYlRSk2cOHHv3r3So+s/+nmEN2/edDgcQ4YMMabRjBkzysvL/duU0+nMyclRSqWkpNjtdrfbHdyhRpT29vYtW7aMHj3aeN0mTZrkdDq11j/++OP48eOVUrGxsWazuaWlRXqk4dPRoTdv1s3NXbdYLFopvXhx1y0tLVopvXbtbXf49deuOxw40HWHX37ReXk6L09nZGiTqfPvmzb5MKRIj9CYRqNGjeoxjQJx/vz5kpISY4OzZs06duxYUIYaUdxut9PpHDNmjPFtTpgwwel0ejwe7x1u3Lhhs9kSExOVUqNGjfr+++8FRxsebrd2OnVBgVZK//3vXbcbjcXF6dOnO2/xKUKvfvibsMc0mjhxYo9pFKDt27cPGzZMKZWcnGyz2W7evBmsLcsyXreCggLjdXvkkUe2bNly69atXu985MiR6dOne49aXbx4McyjDQ+3W3/+uf7DH7RSWildUKC3bu36qsWihwzReXl6+fLOW4iw92kUiv3G33//3Ww2G2+QJk2atH///qA/RTh5PJ7y8vLJkycbr9vIkSMdDsfd8vPq6OgoKyszFovk5eUFvqMRaVwuPW1aZ34jRmiHQ3d03HYHi0Xn5Oh//EObTLqxUesHPMIe02jUqFF9mUYB2rVrlxF8fHx8aWlpa2trSJ8uRFwul7E4wTj04nA4OnrMtXuqrq6eO3eu8fCioqK6urrQDTVsXC5dWNiZX36+LivTvR4Ptlh0Wpq+ckWnpem//U3ru0S4c6c+c6bzT3l5LxH6LdAI3W73Bx98sHTpUpfLFeCmuk+jESNG+DqNAnH9+nWr1RoXF6eUevjhh3066CrO5XIVFhYar1t+fn5ZWZl/5x6MEzkZGRlKqYEDBzocjiDu/IdZRYWeM6czv9xcbbfrGzfuemeLRaemaq31ypU6K0u3tvYe4Z1/IiXCd955x/jnj4uL+/nnn/3bSLCmUQ/79u3z6Z1e1K10q6io8K5DyM3NtdvtN+4x1/qmvr7+pZdeMrb5xBNPVFVVBWWoYbN7t547tzOSnBxtt+vr1+/zEG+EtbU6Pl7/61+9R/jvf+v//Kfzzz//GUkRLliwwLsMdf369b4+PBTTyFBXVzdgwIAJEyb4dCqsx0q3bdu2BWUwQbd79+6nn37aeN1ycnLsdvu1a9eCuH2n05mbm6uUMplMdrs91O8IgmLPHl1U1JlfVpa22fTVq316oDdCrfWf/qTz8/Xly/68J/RboBGuW7fOmApJSUmVlZV9f+Du3bu9b0KMaXT9vj+yfHHo0CHjnV5cXNzKlSt9eqdXWVk5Y8YM7zHDRuPdemTYu3dvUVGRMbasrCybzXa1j3PNR5cvXzabzcYTTZ06tfui3Ehz9KguLtYxMVopnZ6urVZ95YoPD+8e4dGjWim9YUNURai1/uyzz9588827Ld280549e8IzjQI5FeZ2ux0OR1pamoqYlW5Hjx4tLi42juWmpaVZrdYrPs01v3zzzTfDhw9XSiUkJETgSrfKyq78UlO11aovX/Z5I90j1FovWKAnTNDp6VEVYd91n0bp6enhmUaBnAqLkJVulZWV3tctNTXVarVe9mOu+evq1aulpaXGRzQjZ6XbsWO6pETHxWmldEqKLi3V58/7s52TJ/X48TolpeuWHTt6HnfpJxHKTqMAT4U5nc7s7GwlsdLt+PHjJSUlxmHblJSU0tLShoaGsD17d5Gz0q2qquqvfz0dG6uV0snJesUKP/M7dUr/+c86Pl4rpePjb/uScWKj/0R4+vTpRYsWGT9HU1JS3njjjaamplA/aa8CORXWfaXb7Nmzw7DS7cyZM2azOT4+XimVmJhoNpvPnTsX6ie9N/GVbjU1NcZrMnLksyaTNpu1f6cza2t1aalOStJK6YQEXVKiT50K9lh9EfIIq6qq4uPjjWkk/jG2AE+FlZeXh2Glm3eqGe/EzGbz2bNnQ/FE/hFZ6VZbW7t06VKj/4SEhNdee62mpvn+D7vDhQvaatXJyVopHRuri4tv+/0mJRy7ox9//HFErcDocSrs5MmTfX9sSFe6nT17trS01LjASWxsbHFxcXV1dRC3HyzG7r1xPZFBgwZ98cUXoXuuxsZGq9VqMpm8r4l/py6bmrTVqk2mrvxOnAj6YP0UWcvWwimQU2FBX+l251Tz6UeDiOrq6qeeeip0K92ampq8lw4yXpPjx4/7sZ2LF7XNpjMytFI6JkYXFelDh4I70kD5HOH169fXrFlTUFCQlJSUn5+/ZMmSU7fvUL/wwgvZ2dnZ2dlDhw4N3jhDIpBTYcFa6XbnVaoOHz7s36bCL0Qr3S5dumSz2YzNGq+Jf2cpm5u13a4HDOg82jlvnvZ3TVdo+Rah2+02fvjNnz//vffes1gsaWlpubm5v3bbs7506VJDQ0NDQ8N5/w5ahV0gp8IOHjw4depUY66YzWafTnje+ypVUaS+vn7hwoVBWenW90sH3W872m7XmZld+UXyJ2R8i3Dz5s1KqaVLl3pvOXz4sMlkevHFF4M9sLBqbW21Wq3GIdwwrHQL1lSLKAGudGttbbXb7VlZWd5D0Pe9dFCvrl3TZWU6L68zv9mz9f/+58dmwsq3COfPn6+U6nGw7rvvvouo4y5+C+RUWB9Xut15laqdO3cGafjyuu/ez5gxo4/LGO+8dNCOHTv8ePabN7XDoQcP7sxv1iz93//6sRkBvkU4aNCgIUOGhGgokSB0K90CvEpVFOn77n2wLh3U3q4dDj10aGd+jz+u/b0CkQzfIkxISJg8eXKIhhI5Alzp9swzz3Rf6RbEq1RFi/uudAvWpYPa29s3bNjwxz/+YuQ3bZrevj0Y30B4+RZhYmLiuHHjQjSUiBLISjePx7Np06bMzExjjZ7xTkkpNX369G+//TZ0Y440FRUVve7enzt3buTIkcZrMmXKlK+++sqPY6rGNVDGjh2rlMrKKnj88Vvbtuko/RCybxEOHz48MzMzREOJQIGvdMvOzs7JybnzYmcPCGP3PiEhocfu/cyZM/2+dJCR37hx44x/l/Hjx9/jSlZRwbcIX375ZaXUodtPdn766adr1qwJ1odxI02Ap8Lq6+urqqoewPy6O3z4cI/d+4aGBj/yMy5BNGXKFGNTxpWswnYNlNDxLcKvv/5aKfXKK694b6mpqcnNzZ05c2awBxZZov2iD+I6OjrsdruxIs+/lW4ul+vRRx81/gn8uJJVJPN5xcySJUuUUnPnzn3//fetVmt2dnZWVtavkbAMNvSi8aIPEcW/lW4ul+uxxx4zHjVs2LCysrJ+ttvlc4S3bt1at27dpEmTkpOTMzIyFi1a9Ntvv4ViZJGpsbFx8eLFxoRYG6zPkz1I3G73hx9+aPznPHl5efdedltRUeGN1rgEUXCvgRIhHtwF3IHYvn17YWFhOD+X3M+cO3du4cKFNpvtbnf46aef5s2bZ+SXnZ1ts9mam/357FJU6D//XTaijtvtNlbAd7d///61a9caRx/S09Nff/311atXe1fY9ktEiEhRWVn59ttvb926VWudlpa2fPnyVatWeVfY9mPx0gMA1LFjx+x2+yeffOLxeFJTU1999dW33nrLu8K23+M3IYTV1taOHj3a7XabTKZly5atWrXKu8boAUGEkFdSUpKZmbl69Wrvfyf8QCFCQFis9ACABx0RAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEgjAgBYUQICCNCQBgRAsKIEBBGhIAwIgSEESEg7P9nu820aqimUwAAAABJRU5ErkJggg==\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}}]},{"cell_type":"markdown","metadata":{"id":"2R5K7Y5hedbW","colab_type":"text"},"source":["Finally, we can compare generated molecules with our training data via a [similarity search](https://medium.com/gsi-technology/rdkit-for-newbies-3697e617521f) with Tanimoto similarity. This gives an indication of how \"original\" the generated samples are, versus simply producing samples that are extremely similar to molecules the model has already seen. We have to keep in mind that QM9 contains *all* stable small molecules with up to 9 heavy atoms (CONF). So anything new we generate either exists in the full QM9 dataset, or else will not obey the charge neutrality and stability criteria used to generated QM9."]},{"cell_type":"code","metadata":{"id":"RE_vIKDke3Vd","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946559,"user_tz":240,"elapsed":1130347,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["from rdkit.Chem.Fingerprints.FingerprintMols import FingerprintMol\n","from rdkit.DataStructs import FingerprintSimilarity\n","from IPython.display import display\n","\n","def tanimoto_similarity(database_mols, query_mol):\n","    \"\"\"Compare generated molecules to database by Tanimoto similarity.\"\"\"\n","    # convert Mol to datastructure type\n","    fps = [FingerprintMol(m) for m in database_mols]\n","    \n","    # set a query molecule to compare against database\n","    query = FingerprintMol(query_mol)\n","    \n","    similarities = []\n","    \n","    # loop through to find Tanimoto similarity\n","    for idx, f in enumerate(fps):\n","        # tuple: (idx, similarity)\n","        similarities.append((idx, FingerprintSimilarity(query, f)))\n","    \n","    # sort sim using the similarities\n","    similarities.sort(key=lambda x:x[1], reverse=True)\n","    \n","    return similarities"],"execution_count":32,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"cCPEN3_cfQ4N","colab_type":"text"},"source":["We'll consider our generated molecules and look at the top 3 most similar molecules from the training data by Tanimoto similarity. Here's an example where the Tanimoto similarity scores are low! There are no molecules in our training set that are very similar to our generated sample. This might be interesting, or it might mean that the generated molecule is unrealistic."]},{"cell_type":"code","metadata":{"id":"MjR0O1EucwC3","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600973946741,"user_tz":240,"elapsed":1130525,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["train_mols = [Chem.MolFromSmiles(smiles) for smiles in train_smiles]"],"execution_count":33,"outputs":[]},{"cell_type":"code","metadata":{"id":"vsaSkVJufGDy","colab_type":"code","colab":{},"executionInfo":{"status":"ok","timestamp":1600976249046,"user_tz":240,"elapsed":855,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}}},"source":["# change the second argument to compare different generated molecules to QM9\n","tanimoto_scores = tanimoto_similarity(train_mols, gen_mols[3])\n","similar_mols = []"],"execution_count":40,"outputs":[]},{"cell_type":"code","metadata":{"id":"zgyJ9txQsRxg","colab_type":"code","colab":{"base_uri":"https://localhost:8080/","height":967},"executionInfo":{"status":"ok","timestamp":1600976249858,"user_tz":240,"elapsed":370,"user":{"displayName":"Nathan Frey","photoUrl":"https://lh3.googleusercontent.com/a-/AOh14GiCEtTj6AL3entEShxjitkGUQo5YhZ7CJA0917VzA=s64","userId":"14838914823565259795"}},"outputId":"8c07c35c-a575-4919-dd0e-c89ec7a1d6e2"},"source":["for idx, ts in tanimoto_scores[:3]:\n","    print(round(ts, 3))\n","    similar_mols.append(train_mols[idx])\n","\n","display_images(mols_to_pngs(similar_mols, 'qm9_mol'))"],"execution_count":41,"outputs":[{"output_type":"stream","text":["0.243\n","0.243\n","0.241\n"],"name":"stdout"},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}},{"output_type":"display_data","data":{"image/png":"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7d+///7774+JiWnxgzs7wshIxcXJ7VZsrJpyhw2gBZ0/f96zgFpeXl779u1b/PjOjlBSSooiI7Vvn/7yF+tR0EalpaVVVFQ8+eSTY8eO9cXxHR9hWJg874PnzlVlpfU0aHMOHTq0YsWKpi6q3SSOj1DSpEkaNkynTys723oUtDnx8fF1dXUzZswYMGCAj07hDxG6XCosVFCQcnN15Ij1NGhDNm7cuGXLloiIiPnz5/vuLP4QoaRBgzRpkmpqlJRkPQraipqamsTEREkZGRlNXVS7SfwkQkk5OerSRRs2aOtW61HQJuTl5R05cqRfv37Tp0/36Yn8J8KePTV3riTFxamuznoaBLjTp08vXrxYzV1Uu0n8J0JJc+bol7/UwYMqKrIeBQEuOTm5srJy7Nixzz33nK/P5VcRtm8vz2oCqamqqLCeBgHrm2++Wb16dWhoaE5OTiuczq8ilPS73+mZZ3TuHMt1w0fcbrdnUe24uLhmL6rdJP4WoaT8fIWE6O23f+rGGEBzrV27dvv27T179kxOTm6dM/phhP37649/VF2d4uKsR0GguXTpUkpKiqTFixffzqLaTeKHEUpauLA+MvLD2tqPN2+2HgUBZcmSJeXl5QMHDpw8eXKrndQ/I+ze/Z05c367bVtsfHwNy3WjhRw/fjw3N9flcnnWsGi18/pnhNK0116LiooqLS196623rGdBgEhISKiurn7ppZdGjBjRmuf11whDQkLy8/MlZWRknDx50noc+L2dO3d+8MEHHTt2XLRoUSuf2l8jlPT000+PHj364sWL8+bNs54F/q2+vj42NtbtdiclJd17772tfHY/jlBSQUFB+/bt33333b1791rPAj+2cuXKvXv39u7dOyEhofXP7t8R9unT5/XXX2+8N4D1OPBLjS+mcnNzw8LCWn8A/45Q0vz583v16rVr167333/fehb4pQULFpw6dWro0KHjx483GcDvIwwPD8/IyJCUkJBQVVVlPQ78TFlZ2dKlS4OCggoLC1twUe0m8fsIJU2dOjU6OrrxzqnArYuNjb1y5YrnW8hqhkCIsPF/Y557iFuPA7/x6aefbt68ufHFlJVAiFDS0KFDX3jhhcYL/4CfVVdX51lUe968eb169TKcJEAi1LVfba1bt2779u3Ws8APLF269MCBA3369Jk1a5btJIET4T333JOQkOB2u2NiYupZrhs/6dy5c577ung+arYdJnAilOS53KGkpOS9996zngWOlpqaWlFR8dRTT40ePdp6lsCKsPHCv+Tk5AsXLliPA4c6ePBgUVFRSEiI7xbVbpKAilCS5xL4M2fOeJbKAv6/uLi4urq6mTNnRkVFWc8iBV6ELperoKAgKCgoPz+/tLTUehw4zvr167du3XrnnXempaVZz9Ig0CKU9Oijj06ePLlx+WSgUU1NTVJSkqTMzMxu3bpZj9MgACPUtQVCPvzwwy1btljPAgfJzc0tKyvr16/fq6++aj3LdYEZYeNSWXFxcbW1tdbjwBFOnz6dnZ0tKT8/PyQkxHqc6wIzQkmeRSMPHz68YsUK61ngCImJiZWVlePGjXv22WetZ7lBwEbYuHzyvHnzzp49az0OjO3bt2/NmjWhoaFLPIu4O0nARijJcyOBxhuOo81qvI4qISGhb9++1uPcLJAj1LVb6ixfvnz//v3Ws8DMmjVrdu7c2bNnz7meG3s5TIBH6Lm53NWrV2NjY61ngY3q6urU1FRJ2dnZXbp0sR7HiwCPUNdus/r5559v3LjRehYYWLRoUXl5+aBBg15++WXrWbwL/Agbbzg+Z86cK1euWI+DVlVeXp6fn994HZX1ON45dKyWNWPGjAEDBnz33XcOuWAXrSY+Pr66unrSpEnDhw+3nuVHtYkIg4ODPfllZWWxXHfbsWPHjg0bNoSFhXn+dNC53G3G2LFjg4ODrf+90doWLlxo/a33M1zuNrNm7tGjR7dt2zZlyhTrQdB6Pvroo5EjR3bs2NF6kJ/ShiJss6ZNm1ZUVLRixYpp06ZZzwIv2sR7QsDJiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsAYEQLGiBAwRoSAMSIEjBEhYIwIAWNECBgjQsBYiPUA8LnBgwdXVVX17dvXehB453K73dYzAG0aL0cBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGCNCwBgRAsaIEDBGhIAxIgSMESFgjAgBY0QIGPsfwQFEbwWo3UAAAAAASUVORK5CYII=\n","text/plain":["<IPython.core.display.Image object>"]},"metadata":{"tags":[]}}]},{"cell_type":"markdown","metadata":{"id":"5oyYuK11xxBO","colab_type":"text"},"source":["### Further reading\n","\n","So far we have looked at a measure of validity and done a bit of investigation into the novelty of the generated compounds. There are more dimensions along which we can and should evaluate the performance of a generative model. For an example of some standard benchmarks, see the [GuacaMol evaluation framework](https://arxiv.org/pdf/1811.09621.pdf).\n","\n","For examples of normalizing flow-based molecular graph generation frameworks, check out the [MoFlow](https://arxiv.org/abs/2006.10137), [GraphAF](https://arxiv.org/pdf/2001.09382.pdf), and [GraphNVP](https://arxiv.org/pdf/1905.11600.pdf) papers."]},{"cell_type":"markdown","metadata":{"id":"YdJAF3aEHGbV","colab_type":"text"},"source":["# Congratulations! Time to join the Community!\n","\n","Congratulations on completing this tutorial notebook! If you enjoyed working through the tutorial, and want to continue working with DeepChem, we encourage you to finish the rest of the tutorials in this series. You can also help the DeepChem community in the following ways:\n","\n","## Star DeepChem on [GitHub](https://github.com/deepchem/deepchem)\n","This helps build awareness of the DeepChem project and the tools for open source drug discovery that we're trying to build.\n","\n","## Join the DeepChem Gitter\n","The DeepChem [Gitter](https://gitter.im/deepchem/Lobby) hosts a number of scientists, developers, and enthusiasts interested in deep learning for the life sciences. Join the conversation!"]}]}